package com.alifi.crm.dal;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.MalformedURLException;
import java.util.Properties;

import com.alibaba.common.lang.StringUtil;
import com.alibaba.common.lang.io.StreamUtil;
import com.alibaba.common.logging.spi.log4j.DOMConfigurator;

import com.alibaba.service.DefaultServiceManager;
import com.alibaba.service.ServiceInitializationException;
import com.alibaba.service.ServiceManager;
import com.alibaba.service.resource.ResourceLoaderService;
import com.alibaba.service.spring.BeanFactoryService;

/**
 * 用来初始化service manager及相关的服务的辅助类。
 *
 * @author Michael Zhou
 */
public class ServiceTestStarter {
    private Params         params  = new Params();
    private File           basedir;
    private File           srcdir;
    private File           destdir;
    private ServiceManager manager;

    /* =========================================================== */
    /*  初始化参数                                                 */
    /* =========================================================== */
    protected class Params {
        public boolean   initAll   = true;
        public String    basedirProperty = "project.home";
        public String    basedir   = ".";
        public String    srcdir    = "src/conf.test";
        public String    destdir   = "target/test";
        public String    log4jConfig = "log4j.xml"; // 相对于srcdir
        public String    resourceConfig = "resources.xml"; // 相对于srcdir
        public String    serviceConfig = "services.xml"; // 相对于srcdir
        public Exception initException = null;
        public boolean   started   = false;
        public boolean   autoCreateSampleConfigurations = false;
    }

    public ServiceTestStarter setBasedir(String basedir) {
        getParams().basedir = basedir;
        return this;
    }

    public ServiceTestStarter setBasedirProperty(String basedirProperty) {
        getParams().basedirProperty = basedirProperty;
        return this;
    }

    public ServiceTestStarter setDestdir(String destdir) {
        getParams().destdir = destdir;
        return this;
    }

    public ServiceTestStarter setInitAll(boolean initAll) {
        getParams().initAll = initAll;
        return this;
    }

    public ServiceTestStarter setInitException(Exception initException) {
        getParams().initException = initException;
        return this;
    }

    public ServiceTestStarter setSrcdir(String srcdir) {
        getParams().srcdir = srcdir;
        return this;
    }

    public ServiceTestStarter setLog4jConfig(String log4jConfig) {
        getParams().log4jConfig = log4jConfig;
        return this;
    }

    public ServiceTestStarter setResourceConfig(String resourceConfig) {
        getParams().resourceConfig = resourceConfig;
        return this;
    }

    public ServiceTestStarter setServiceConfig(String serviceConfig) {
        getParams().serviceConfig = serviceConfig;
        return this;
    }

    protected final Params getParams() {
        if (params == null) {
            throw new IllegalStateException();
        }

        return params;
    }

    /* =========================================================== */
    /*  执行初始化                                                 */
    /* =========================================================== */
    public ServiceTestStarter start() {
        return start(true);
    }

    public ServiceTestStarter start(boolean autoCreateSampleConfigurations) {
        Params params = getParams();

        if (params.started) {
            return this;
        }

        params.started                        = true;
        params.autoCreateSampleConfigurations = autoCreateSampleConfigurations;

        try {
            setupDirectories();
            autoCreateSampleConfigurations();
            setupLog4j();
            setupServiceManager();
        } catch (Exception e) {
            params.initException = e;
        }

        return this;
    }

    /**
     * 初始化basedir, srcdir和destdir等。
     */
    private void setupDirectories() throws IOException {
        Params params          = getParams();
        String basedirProperty = System.getProperty(params.basedirProperty);

        basedir                = StringUtil.isEmpty(basedirProperty)
                                 ? new File(params.basedir).getCanonicalFile()
                                 : new File(basedirProperty).getCanonicalFile();

        System.setProperty(params.basedirProperty, basedir.getAbsolutePath());

        srcdir  = new File(basedir, params.srcdir);
        destdir = new File(basedir, params.destdir);

        boolean srcdirCreated = false;

        if (!srcdir.exists() && params.autoCreateSampleConfigurations) {
            srcdir.mkdirs();
            srcdirCreated = true;
        }

        if (!srcdir.isDirectory() || !srcdir.exists()) {
            throw new IllegalArgumentException("Source directory does not exist: " + srcdir);
        }

        if (!destdir.exists()) {
            destdir.mkdirs();
        }

        if (!destdir.isDirectory() || !destdir.exists()) {
            throw new IllegalArgumentException("Destination directory does not exist: " + destdir);
        }

        System.out.println("Set base dir to:          " + basedir);

        if (srcdirCreated) {
            System.out.println("Set source dir to:        " + srcdir + " (auto created)");
        } else {
            System.out.println("Set source dir to:        " + srcdir);
        }

        System.out.println("Set destination dir to:   " + destdir);
        System.out.println();
    }

    /**
     * 自动创建示范配置文件。
     */
    private void autoCreateSampleConfigurations() {
        Params params = getParams();

        if (params.autoCreateSampleConfigurations) {
            int count = 0;

            // 创建log4j配置文件
            count += createIfNotExist(srcdir, params.log4jConfig, "sample/log4j-sample.xml");

            // 创建resources配置文件
            count += createIfNotExist(srcdir, params.resourceConfig, "sample/resources-sample.xml");

            // 创建services配置文件
            count += createIfNotExist(srcdir, params.serviceConfig, "sample/services-sample.xml");

            if (count > 0) {
                System.out.println(count + " sample files successfully created.\n");
            }
        }
    }

    private int createIfNotExist(File basedir, String name, String srcfile) {
        File destfile = new File(basedir, name);
        int  created  = 0;

        if (!destfile.exists()) {
            if (!destfile.getParentFile().exists()) {
                destfile.getParentFile().mkdirs();
            }

            System.out.println("Auto-create sample file:  " + destfile.getAbsolutePath());

            InputStream  is = null;
            OutputStream os = null;

            try {
                is = getClass().getResourceAsStream(srcfile);
                os = new FileOutputStream(destfile);

                StreamUtil.io(is, os);
                created = 1;
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                if (is != null) {
                    try {
                        is.close();
                    } catch (IOException e) {
                    }
                }

                if (os != null) {
                    try {
                        os.close();
                    } catch (IOException e) {
                    }
                }
            }
        }

        return created;
    }

    /**
     * 初始化log4j.xml，可用${basedir}, ${srcdir}, ${destdir}。
     */
    private void setupLog4j() {
        Params params = getParams();

        try {
            Properties props = new Properties();

            props.put("basedir", basedir.getAbsolutePath());
            props.put("srcdir", srcdir.getAbsolutePath());
            props.put("destdir", destdir.getAbsolutePath());

            DOMConfigurator.configure(new File(srcdir, params.log4jConfig).toURL(), props);
        } catch (MalformedURLException e) {
        }
    }

    /**
     * 初始化service manager。
     */
    private void setupServiceManager() throws ServiceInitializationException {
        Params params = getParams();

        manager = new DefaultServiceManager(srcdir.getAbsolutePath(), params.serviceConfig,
                                            params.initAll);
    }

    /* =========================================================== */
    /*  取得值                                                     */
    /* =========================================================== */
    public File getBasedir() {
        assertStart();
        return basedir;
    }

    public File getSrcdir() {
        assertStart();
        return srcdir;
    }

    public File getDestdir() {
        assertStart();
        return destdir;
    }

    public ServiceManager getServiceManager() {
        assertStart();
        return manager;
    }

    public ResourceLoaderService getResourceLoaderService() {
        assertStart();
        return (ResourceLoaderService) manager.getService(ResourceLoaderService.SERVICE_NAME);
    }

    public BeanFactoryService getBeanFactoryService() {
        assertStart();
        return getBeanFactoryService(BeanFactoryService.SERVICE_NAME);
    }

    public BeanFactoryService getBeanFactoryService(String name) {
        assertStart();
        return (BeanFactoryService) manager.getService(name);
    }

    protected final void assertStart() {
        if (params != null) {
            if (!params.started) {
                throw new IllegalStateException("Not start() yet!");
            }

            Exception initException = params.initException;

            params = null;

            if (initException != null) {
                if (initException instanceof RuntimeException) {
                    throw (RuntimeException) initException;
                } else {
                    throw new RuntimeException(initException);
                }
            }
        }
    }
}
